package handler

import (
	"context"
	"encoding/json"
	"fmt"
	goods_pb "gitee.com/fangy1111/shop/goodsPb"
	"gitee.com/fangy1111/shop/inventory"
	"gitee.com/fangy1111/shop/order"
	"github.com/apache/rocketmq-client-go/v2"
	"github.com/apache/rocketmq-client-go/v2/consumer"
	"github.com/apache/rocketmq-client-go/v2/primitive"
	"github.com/apache/rocketmq-client-go/v2/producer"
	"github.com/opentracing/opentracing-go"
	uuid "github.com/satori/go.uuid"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gorm.io/gorm"
	"order_srv/global"
	"order_srv/model"
	"order_srv/pkg"
	"os"
	"strconv"
	"time"
)

type OrderService struct {
	order.UnimplementedOrderServer
}

// 购物车列表
func (o OrderService) CartItemList(ctx context.Context, request *order.UserInfo) (*order.CartItemListResponse, error) {
	var cart []*model.ShoppingCart

	res := global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
		UserId: request.Id,
	}).Find(&cart)

	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, "查询列表失败")
	}
	var count int64

	global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
		UserId: request.Id,
	}).Count(&count)

	var shopCart []*order.ShopCartInfoResponse

	for _, v := range cart {
		shopCart = append(shopCart, &order.ShopCartInfoResponse{
			Id:      int32(v.ID),
			UserId:  v.UserId,
			GoodsId: v.GoodsId,
			Nums:    v.Nums,
			Checked: v.Checked,
		})
	}

	return &order.CartItemListResponse{
		Total: int32(count),
		Data:  shopCart,
	}, nil
}

// 添加购物车
func (o OrderService) CreateCartItem(ctx context.Context, request *order.CartItemRequest) (*order.ShopCartInfoResponse, error) {
	var cart model.ShoppingCart
	res := global.DB.Model(&model.ShoppingCart{}).Where("user_id = ? and goods_id =?", request.UserId, request.GoodsId).First(&cart)

	if res.RowsAffected == 0 {
		cart.UserId = request.UserId
		cart.GoodsId = request.GoodsId
		cart.Nums = request.Nums
		cart.Checked = true
		tx := global.DB.Model(&model.ShoppingCart{}).Create(&cart)
		if tx.Error != nil {
			return nil, status.Errorf(codes.Internal, "添加购物车失败")
		}
	} else {
		cart.Nums += request.Nums
		tx := global.DB.Model(&model.ShoppingCart{}).Where("user_id = ? and goods_id = ?", request.UserId, request.GoodsId).Updates(&cart)
		if tx.Error != nil {
			return nil, status.Errorf(codes.Internal, "添加购物车失败")
		}
	}
	return &order.ShopCartInfoResponse{
		Id:      int32(cart.ID),
		UserId:  cart.UserId,
		GoodsId: cart.GoodsId,
		Nums:    cart.Nums,
		Checked: cart.Checked,
	}, nil
}

// 更新购物车
func (o OrderService) UpdateCartItem(ctx context.Context, request *order.CartItemRequest) (*order.OrderEmpty, error) {
	var cart model.ShoppingCart

	res := global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
		Model: gorm.Model{
			ID: uint(request.Id),
		},
	}).First(&cart)
	if res.RowsAffected == 0 {
		return nil, status.Errorf(codes.Internal, "购物车没有该数据")
	}

	cart.Nums = request.Nums
	cart.Checked = request.Checked
	tx := global.DB.Save(&cart)

	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "更新购物车失败")
	}

	return &order.OrderEmpty{}, nil
}

// 删除购物车
func (o OrderService) DeleteCartItem(ctx context.Context, in *order.CartItemRequest) (*order.OrderEmpty, error) {
	var cart model.ShoppingCart

	res := global.DB.Model(&model.ShoppingCart{}).Where("id = ?", in.Id).Delete(&cart)
	if res.Error != nil {
		return nil, status.Errorf(codes.Internal, "删除购物车失败")
	}
	return &order.OrderEmpty{}, nil
}

// 创建订单
//func (o OrderService) Create(ctx context.Context, request *order.OrderRequest) (*order.OrderInfoResponse, error) {
//	//从购物车中获取到选中的商品
//	//定义一个切片来保存购物车商品数据
//	var cart []model.ShoppingCart
//	res := global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
//		UserId:  request.UserId,
//		Checked: true,
//	}).Find(&cart)
//	if res.Error != nil {
//		return nil, status.Errorf(codes.Internal, "查询购物车失败")
//	}
//
//	//定义一个切片来保存查到的商品id
//	//定义商品数据字典
//	goodsNumsMap := make(map[int32]int32)
//	var goodsIds []int32
//	for _, v := range cart {
//		goodsIds = append(goodsIds, v.GoodsId)
//		goodsNumsMap[v.GoodsId] = v.Nums
//	}
//
//	//拿到了商品id去跨微服务调用获取商品信息的接口
//	goods, err := global.GoodsService.BatchGetGoods(context.Background(), &goods_pb.BatchGoodsIdInfo{Id: goodsIds})
//	if err != nil {
//		return nil, status.Errorf(codes.Internal, "商品信息查询失败")
//	}
//
//	//订单总金额 = 所有商品的金额加一起 (商品的价格（goods）*购物车商品的数量(shopCarts.nums))
//	var orderMount float32
//	//定义一个切片 保存扣减商品和信息数量
//	var goodsInfo []*inventory.GoodsInvInfo
//	//定义一个切片 保存订单购买的所有商品
//	var orderGooods []*model.OrderGoods
//
//	for _, v := range goods.Data {
//		orderMount += v.ShopPrice * float32(goodsNumsMap[v.Id])
//
//		orderGooods = append(orderGooods, &model.OrderGoods{
//			GoodsId:    v.Id,
//			GoodsName:  v.GoodsSn,
//			GoodsImage: v.GoodsFrontImage,
//			GoodsPrice: v.ShopPrice,
//			Nums:       goodsNumsMap[v.Id],
//		})
//		goodsInfo = append(goodsInfo, &inventory.GoodsInvInfo{
//			GoodsId: v.Id,
//			Stock:   goodsNumsMap[v.Id],
//		})
//	}
//
//	//库存扣减
//	_, err = global.InventoryService.Sell(context.Background(), &inventory.SellInfo{
//		GoodsInfo: goodsInfo,
//	})
//	if err != nil {
//		return nil, status.Errorf(codes.Internal, "库存扣减失败")
//	}
//
//	//开启事务
//	tx := global.DB.Begin()
//	//创建订单基本信息表
//	info := model.OrderInfo{
//		UserId:       request.UserId,
//		OrderSn:      fmt.Sprintf("%s", uuid.NewV4()),
//		Status:       1,
//		OrderMount:   orderMount,
//		Address:      request.Address,
//		SignerName:   request.Name,
//		SingerMobile: request.Mobile,
//		Post:         request.Post,
//		PayTime:      time.Now(),
//	}
//	ress := tx.Create(&info)
//	if ress.Error != nil {
//		return nil, status.Errorf(codes.Internal, "创建订单失败")
//	}
//	//创建订单商品表
//	for _, orderGood := range orderGooods {
//		orderGood.OrderId = int32(info.ID)
//	}
//	//多条数据，最好不要循环入库，来使用批量入库
//	if result := tx.CreateInBatches(orderGooods, 100); result.RowsAffected == 0 {
//		tx.Rollback()
//		return nil, status.Errorf(codes.Internal, "创建订单商品失败")
//	}
//
//	//从购物车中删除已购买的记录
//	if result := tx.Where(&model.ShoppingCart{UserId: request.UserId, Checked: true}).Delete(&cart); result.RowsAffected == 0 {
//		tx.Rollback()
//		return nil, status.Errorf(codes.Internal, "删除购物车商品失败")
//	}
//	tx.Commit()
//
//	return &order.OrderInfoResponse{
//		Id:      int32(info.ID),
//		UserId:  info.UserId,
//		OrderSn: info.OrderSn,
//		PayType: strconv.Itoa(int(info.PayType)),
//		Status:  strconv.Itoa(int(info.Status)),
//		Post:    info.Post,
//		Total:   info.OrderMount,
//		Address: info.Address,
//		Name:    info.SignerName,
//		Mobile:  info.SingerMobile,
//	}, nil
//}

//// 创建订单
//func (o OrderService) Create(ctx context.Context, request *order.OrderRequest) (*order.OrderInfoResponse, error) {
//	//1.获取购物车中选中的商品
//	//创建一个用于存储购物车商品信息的切片
//	var cart []*model.ShoppingCart
//
//	//判断购物车是否有这件商品
//	res := global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
//		UserId:  request.UserId,
//		Checked: true,
//	}).Find(&cart)
//
//	if res.Error != nil {
//		return nil, status.Errorf(codes.Internal, "获取购物车信息失败")
//	}
//
//	//存储扣减库存信息的切片
//	var goodsInfo []*inventory.GoodsInvInfo
//
//	//创建一个存放商品id的切片
//	var goodsIds []int32
//	//订单商品的切片
//	var orderGoods []*model.OrderGoods
//	//循环购物车 拿到购物车里面的商品id和商品数量
//	for _, v := range cart {
//		//将获取到的商品id追加到goodsids切片
//		goodsIds = append(goodsIds, v.GoodsId)
//
//		//用于扣减库存的切片
//		goodsInfo = append(goodsInfo, &inventory.GoodsInvInfo{
//			GoodsId: v.GoodsId,
//			Stock:   v.Nums,
//		})
//		//用于创建订单商品表的切片
//		orderGoods = append(orderGoods, &model.OrderGoods{
//			GoodsId: v.GoodsId,
//			Nums:    v.Nums,
//		})
//	}
//
//	//跨微服务调用商品服务里面的接口（用多个id查询商品的数据）
//	goods, err := global.GoodsService.BatchGetGoods(context.Background(), &goods_pb.BatchGoodsIdInfo{Id: goodsIds})
//	if err != nil {
//		return nil, status.Errorf(codes.Internal, "获取商品信息失败")
//	}
//
//	//循环查询到的商品数据，组装订单商品表里面的字段（注意！这里不能用append追加了，因为上面的orderGoods切片已经有我们需要的数据，再次追加会覆盖掉我们的商品id和商品数量，265行——267行）
//	for k, v := range goods.Data {
//		orderGoods[k].GoodsName = v.Name             //商品名称
//		orderGoods[k].GoodsPrice = v.ShopPrice       //商品价格
//		orderGoods[k].GoodsImage = v.GoodsFrontImage //商品封面
//	}
//	//订单总金额
//	var orderMount float32
//	//这里循环我们已经组装好的订单商品切片 计算出我们的订单总金额 （商品单价*商品数量）
//	for _, v := range orderGoods {
//		orderMount += v.GoodsPrice * float32(v.Nums)
//	}
//
//	//扣减商品库存
//	_, err = global.InventoryService.Sell(context.Background(), &inventory.SellInfo{
//		GoodsInfo: goodsInfo,
//	})
//	if err != nil {
//		return nil, status.Errorf(codes.Internal, "扣减库存失败")
//	}
//
//	//创建订单表
//	tx := global.DB.Begin() //开启本地事务
//	info := model.OrderInfo{
//		UserId:       request.UserId,
//		OrderSn:      fmt.Sprintf("%s", uuid.NewV4()), //随机生成订单编号，唯一就可
//		Status:       1,
//		OrderMount:   orderMount,
//		PayTime:      time.Now(), //支付时间有点问题，可以去把model层里面的订单信息表的支付时间的类型改成 sql.NullTime就可以默认为空
//		Address:      request.Address,
//		SignerName:   request.Name,
//		SingerMobile: request.Mobile,
//		Post:         request.Post,
//	}
//	create := tx.Model(&model.OrderInfo{}).Create(&info)
//
//	if create.Error != nil {
//		tx.Rollback() //回滚
//		return nil, status.Errorf(codes.Internal, "订单创建失败")
//	}
//
//	//组装我们的订单id
//	for k, _ := range orderGoods {
//		orderGoods[k].OrderId = int32(info.ID)
//	}
//
//	//创建订单商品表
//	batches := tx.Model(&model.OrderGoods{}).CreateInBatches(&orderGoods, 100)
//	if batches.Error != nil {
//		tx.Rollback()
//		return nil, status.Errorf(codes.Internal, "订单商品创建失败")
//	}
//
//	//删除购物车中的商品
//	db := tx.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
//		UserId:  request.UserId,
//		Checked: true,
//	}).Delete(&cart)
//	if db.Error != nil {
//		tx.Rollback()
//		return nil, status.Errorf(codes.Internal, "清空购物车失败")
//	}
//	tx.Commit()
//	return &order.OrderInfoResponse{
//		Id:      int32(info.ID),
//		UserId:  info.UserId,
//		OrderSn: info.OrderSn,
//		PayType: strconv.Itoa(int(info.PayType)),
//		Status:  strconv.Itoa(int(info.Status)),
//		Post:    info.Post,
//		Total:   info.OrderMount,
//		Address: info.Address,
//		Name:    info.SignerName,
//		Mobile:  info.SingerMobile,
//	}, nil
//}

// // 创建订单 tcc版
//
//	func (o OrderService) Create(ctx context.Context, request *order.OrderRequest) (*order.OrderInfoResponse, error) {
//		//1.获取购物车中选中的商品
//		//创建一个用于存储购物车商品信息的切片
//		var cart []*model.ShoppingCart
//
//		//判断购物车是否有这件商品
//		res := global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
//			UserId:  request.UserId,
//			Checked: true,
//		}).Find(&cart)
//
//		if res.Error != nil {
//			return nil, status.Errorf(codes.Internal, "获取购物车信息失败")
//		}
//
//		//存储扣减库存信息的切片
//		var goodsInfo []*inventory.GoodsInvInfo
//
//		//创建一个存放商品id的切片
//		var goodsIds []int32
//		//订单商品的切片
//		var orderGoods []*model.OrderGoods
//		//循环购物车 拿到购物车里面的商品id和商品数量
//		for _, v := range cart {
//			//将获取到的商品id追加到goodsids切片
//			goodsIds = append(goodsIds, v.GoodsId)
//
//			//用于扣减库存的切片
//			goodsInfo = append(goodsInfo, &inventory.GoodsInvInfo{
//				GoodsId: v.GoodsId,
//				Stock:   v.Nums,
//			})
//			//用于创建订单商品表的切片
//			orderGoods = append(orderGoods, &model.OrderGoods{
//				GoodsId: v.GoodsId,
//				Nums:    v.Nums,
//			})
//		}
//
//		//跨微服务调用商品服务里面的接口（用多个id查询商品的数据）
//		goods, err := global.GoodsService.BatchGetGoods(context.Background(), &goods_pb.BatchGoodsIdInfo{Id: goodsIds})
//		if err != nil {
//			return nil, status.Errorf(codes.Internal, "获取商品信息失败")
//		}
//
//		//循环查询到的商品数据，组装订单商品表里面的字段（注意！这里不能用append追加了，因为上面的orderGoods切片已经有我们需要的数据，再次追加会覆盖掉我们的商品id和商品数量，265行——267行）
//		for k, v := range goods.Data {
//			orderGoods[k].GoodsName = v.Name             //商品名称
//			orderGoods[k].GoodsPrice = v.ShopPrice       //商品价格
//			orderGoods[k].GoodsImage = v.GoodsFrontImage //商品封面
//		}
//		//订单总金额
//		var orderMount float32
//		//这里循环我们已经组装好的订单商品切片 计算出我们的订单总金额 （商品单价*商品数量）
//		for _, v := range orderGoods {
//			orderMount += v.GoodsPrice * float32(v.Nums)
//		}
//
//		//扣减商品库存 尝试扣减库存
//		_, err = global.InventoryService.TrySell(context.Background(), &inventory.SellInfo{
//			GoodsInfo: goodsInfo,
//		})
//		if err != nil {
//			return nil, status.Errorf(codes.Internal, "尝试扣减库存失败")
//		}
//		//创建订单表
//		tx := global.DB.Begin() //开启本地事务
//		info := model.OrderInfo{
//			UserId:       request.UserId,
//			OrderSn:      fmt.Sprintf("%s", uuid.NewV4()), //随机生成订单编号，唯一就可
//			Status:       1,
//			OrderMount:   orderMount,
//			PayTime:      time.Now(), //支付时间有点问题，可以去把model层里面的订单信息表的支付时间的类型改成 sql.NullTime就可以默认为空
//			Address:      request.Address,
//			SignerName:   request.Name,
//			SingerMobile: request.Mobile,
//			Post:         request.Post,
//		}
//		create := tx.Model(&model.OrderInfo{}).Create(&info)
//
//		if create.Error != nil {
//			tx.Rollback() //回滚
//			// 取消扣减库存
//			_, err = global.InventoryService.CancelSell(context.Background(), &inventory.SellInfo{
//				GoodsInfo: goodsInfo,
//			})
//			if err != nil {
//				return nil, status.Errorf(codes.Internal, "取消扣减库存失败")
//			}
//			return nil, status.Errorf(codes.Internal, "订单创建失败")
//		}
//
//		//组装我们的订单id
//		for k, _ := range orderGoods {
//			orderGoods[k].OrderId = int32(info.ID)
//		}
//
//		//创建订单商品表
//		batches := tx.Model(&model.OrderGoods{}).CreateInBatches(&orderGoods, 100)
//		if batches.Error != nil {
//			tx.Rollback()
//			// 取消扣减库存
//			_, err = global.InventoryService.CancelSell(context.Background(), &inventory.SellInfo{
//				GoodsInfo: goodsInfo,
//			})
//			if err != nil {
//				return nil, status.Errorf(codes.Internal, "取消扣减库存失败")
//			}
//			return nil, status.Errorf(codes.Internal, "订单商品创建失败")
//		}
//
//		//删除购物车中的商品
//		db := tx.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
//			UserId:  request.UserId,
//			Checked: true,
//		}).Delete(&cart)
//		if db.Error != nil {
//			tx.Rollback()
//			// 取消扣减库存
//			_, err = global.InventoryService.CancelSell(context.Background(), &inventory.SellInfo{
//				GoodsInfo: goodsInfo,
//			})
//			if err != nil {
//				return nil, status.Errorf(codes.Internal, "取消扣减库存失败")
//			}
//			return nil, status.Errorf(codes.Internal, "清空购物车失败")
//		}
//		tx.Commit()
//
//		// 确认扣减库存
//		_, err = global.InventoryService.ConfirmSell(context.Background(), &inventory.SellInfo{
//			GoodsInfo: goodsInfo,
//		})
//		if err != nil {
//			return nil, status.Errorf(codes.Internal, "确认扣减库存失败")
//		}
//
//		return &order.OrderInfoResponse{
//			Id:      int32(info.ID),
//			UserId:  info.UserId,
//			OrderSn: info.OrderSn,
//			PayType: strconv.Itoa(int(info.PayType)),
//			Status:  strconv.Itoa(int(info.Status)),
//			Post:    info.Post,
//			Total:   info.OrderMount,
//			Address: info.Address,
//			Name:    info.SignerName,
//			Mobile:  info.SingerMobile,
//		}, nil
//	}

type OrderListener struct {
	Code        codes.Code
	Detail      string
	Id          int32
	OrderAmount float32
	Ctx         context.Context
}

// ExecuteLocalTransaction这里操作本地事务，做下订单，订单商品和删除购物车
func (o *OrderListener) ExecuteLocalTransaction(msg *primitive.Message) primitive.LocalTransactionState {
	var orderData model.OrderInfo
	//把msg的消息反解到订单信息结构体
	json.Unmarshal(msg.Body, &orderData)
	//1.获取购物车中选中的商品
	//创建一个用于存储购物车商品信息的切片
	var cart []*model.ShoppingCart

	//定义了一个父span
	parentSpan := opentracing.SpanFromContext(o.Ctx)
	shopCartSpan := opentracing.GlobalTracer().StartSpan("select_shopcart", opentracing.ChildOf(parentSpan.Context()))
	//判断购物车是否有这件商品
	res := global.DB.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
		UserId:  orderData.UserId,
		Checked: true,
	}).Find(&cart)
	shopCartSpan.Finish()

	if res.Error != nil {
		zap.S().Error("获取购物车信息失败")
		return primitive.CommitMessageState
	}

	//存储扣减库存信息的切片
	var goodsInfo []*inventory.GoodsInvInfo

	//创建一个存放商品id的切片
	var goodsIds []int32
	//订单商品的切片
	var orderGoods []*model.OrderGoods
	//循环购物车 拿到购物车里面的商品id和商品数量
	for _, v := range cart {
		//将获取到的商品id追加到goodsids切片
		goodsIds = append(goodsIds, v.GoodsId)

		//用于扣减库存的切片
		goodsInfo = append(goodsInfo, &inventory.GoodsInvInfo{
			GoodsId: v.GoodsId,
			Stock:   v.Nums,
		})
		//用于创建订单商品表的切片
		orderGoods = append(orderGoods, &model.OrderGoods{
			GoodsId: v.GoodsId,
			Nums:    v.Nums,
		})
	}

	//跨微服务调用商品服务里面的接口（用多个id查询商品的数据）
	getBatchGoodsInfo := opentracing.GlobalTracer().StartSpan("get_batch_goodsinfo", opentracing.ChildOf(parentSpan.Context()))
	goods, err := global.GoodsService.BatchGetGoods(context.Background(), &goods_pb.BatchGoodsIdInfo{Id: goodsIds})
	if err != nil {
		zap.S().Error("获取商品信息失败")
		return primitive.CommitMessageState
	}
	getBatchGoodsInfo.Finish()

	//循环查询到的商品数据，组装订单商品表里面的字段（注意！这里不能用append追加了，因为上面的orderGoods切片已经有我们需要的数据，再次追加会覆盖掉我们的商品id和商品数量，265行——267行）
	for k, v := range goods.Data {
		orderGoods[k].GoodsName = v.Name             //商品名称
		orderGoods[k].GoodsPrice = v.ShopPrice       //商品价格
		orderGoods[k].GoodsImage = v.GoodsFrontImage //商品封面
	}
	//订单总金额
	var orderMount float32
	//这里循环我们已经组装好的订单商品切片 计算出我们的订单总金额 （商品单价*商品数量）
	for _, v := range orderGoods {
		orderMount += v.GoodsPrice * float32(v.Nums)
	}

	//扣减商品库存
	inventorySell := opentracing.GlobalTracer().StartSpan("inventory_sell", opentracing.ChildOf(parentSpan.Context()))

	_, err = global.InventoryService.Sell(context.Background(), &inventory.SellInfo{
		GoodsInfo: goodsInfo,
		OrderSn:   orderData.OrderSn,
	})
	if err != nil {
		zap.S().Error("扣减库存失败")
		return primitive.CommitMessageState
	}
	inventorySell.Finish()
	//创建订单表
	tx := global.DB.Begin() //开启本地事务
	info := model.OrderInfo{
		UserId:       orderData.UserId,
		OrderSn:      orderData.OrderSn,
		Status:       1,
		OrderMount:   orderMount,
		PayTime:      time.Now(), //支付时间有点问题，可以去把model层里面的订单信息表的支付时间的类型改成 sql.NullTime就可以默认为空
		Address:      orderData.Address,
		SignerName:   orderData.SignerName,
		SingerMobile: orderData.SingerMobile,
		Post:         orderData.Post,
	}

	createOrder := opentracing.GlobalTracer().StartSpan("create_order", opentracing.ChildOf(parentSpan.Context()))
	create := tx.Model(&model.OrderInfo{}).Create(&info)

	o.Id = int32(info.ID)
	o.OrderAmount = info.OrderMount

	if create.Error != nil {
		tx.Rollback() //回滚
		zap.S().Error("订单创建失败")
		return primitive.CommitMessageState
	}
	createOrder.Finish()
	//组装我们的订单id
	for k, _ := range orderGoods {
		orderGoods[k].OrderId = int32(info.ID)
	}

	//创建订单商品表
	createOrderGoods := opentracing.GlobalTracer().StartSpan("create_order_goods", opentracing.ChildOf(parentSpan.Context()))
	batches := tx.Model(&model.OrderGoods{}).CreateInBatches(&orderGoods, 100)
	if batches.Error != nil {
		tx.Rollback()
		zap.S().Error("订单商品创建失败")
		return primitive.CommitMessageState

	}
	createOrderGoods.Finish()
	//删除购物车中的商品
	deleteCart := opentracing.GlobalTracer().StartSpan("delete_cart", opentracing.ChildOf(parentSpan.Context()))
	db := tx.Model(&model.ShoppingCart{}).Where(model.ShoppingCart{
		UserId:  orderData.UserId,
		Checked: true,
	}).Delete(&cart)
	if db.Error != nil {
		tx.Rollback()
		zap.S().Error("清空购物车失败")
		return primitive.CommitMessageState
	}
	deleteCart.Finish()
	//创建延迟队列
	//创建一个生产者实例
	groupName := "delay" + uuid.NewV4().String()
	p, _ := rocketmq.NewProducer(
		producer.WithNsResolver(primitive.NewPassthroughResolver([]string{fmt.Sprintf("%s:%d", global.ServerConfig.Rocketmq.Host, global.ServerConfig.Rocketmq.Port)})),
		//重试次数设置为2
		producer.WithRetry(2),
		producer.WithGroupName(groupName),
	)

	//启动生产者
	err = p.Start()
	if err != nil {
		tx.Rollback()
		zap.S().Info("启动生产者失败")
		return primitive.CommitMessageState
	}

	//创建一条新的消息
	delayMsg := primitive.NewMessage(global.ServerConfig.Rocketmq.DelayTopic, msg.Body)
	//设置消息的延迟等级为4 1分钟
	delayMsg.WithDelayTimeLevel(5)

	//发送消息，并等待同步结果返回
	_, err = p.SendSync(context.Background(), delayMsg)
	if err != nil {
		tx.Rollback()
		zap.S().Info("发送消息失败")
		return primitive.CommitMessageState
	}

	tx.Commit()
	return primitive.RollbackMessageState
}

// 检查本地事务，事务回查机制
func (o OrderListener) CheckLocalTransaction(msg *primitive.MessageExt) primitive.LocalTransactionState {
	var orderData model.OrderInfo
	//反解信息到订单信息结构体
	json.Unmarshal(msg.Body, &orderData)
	//用订单编号查订单信息表是否存在该订单
	res := global.DB.Model(&model.OrderInfo{}).Where(model.OrderInfo{
		OrderSn: orderData.OrderSn,
	}).First(&model.OrderInfo{})
	if res.RowsAffected == 0 {
		zap.S().Error("订单不存在")
		return primitive.CommitMessageState
	}
	return primitive.RollbackMessageState
}

// 创建订单 rocketmq版
func (o *OrderService) Create(ctx context.Context, request *order.OrderRequest) (*order.OrderInfoResponse, error) {
	//订单消息
	info := model.OrderInfo{
		UserId:       request.UserId,
		OrderSn:      fmt.Sprintf("%s", uuid.NewV4()), //随机生成订单编号，唯一就可
		Status:       1,
		PayTime:      time.Now(),
		Address:      request.Address,
		SignerName:   request.Name,
		SingerMobile: request.Mobile,
		Post:         request.Post,
	}
	//序列化数据
	marshal, _ := json.Marshal(info)

	orderListener := OrderListener{Ctx: ctx}

	//创建唯一的生产者组名
	groupName := "productName" + uuid.NewV4().String()
	// 创建生产者
	p, _ := rocketmq.NewTransactionProducer(
		&orderListener, //消息的监听者，必须写上ExecuteLocalTransaction方法这个方法主要是写本地事务逻辑，成功与否来提交半事务消息 CheckLocalTransaction方法是事务回查机制 如果ExecuteLocalTransaction方法一直没有提交半事务消息或者回滚半事务消息，就会走到这个回查方法了
		producer.WithNsResolver(primitive.NewPassthroughResolver([]string{fmt.Sprintf("%s:%d", global.ServerConfig.Rocketmq.Host, global.ServerConfig.Rocketmq.Port)})), //连接rocketmq
		producer.WithRetry(1), //生产消息重试次数
		producer.WithGroupName(groupName),
	)
	// 启动生产者
	err := p.Start()
	if err != nil {
		zap.S().Info("启动生产者失败")
		os.Exit(1)
	}

	// 发送事务消息
	_, err = p.SendMessageInTransaction(context.Background(),
		primitive.NewMessage(global.ServerConfig.Rocketmq.RebackTopic, marshal))

	if err != nil {
		zap.S().Info("发送消息失败")
	}
	return &order.OrderInfoResponse{
		Id:      orderListener.Id,
		UserId:  info.UserId,
		OrderSn: info.OrderSn,
		PayType: strconv.Itoa(int(info.PayType)),
		Status:  strconv.Itoa(int(info.Status)),
		Post:    info.Post,
		Total:   orderListener.OrderAmount,
		Address: info.Address,
		Name:    info.SignerName,
		Mobile:  info.SingerMobile,
	}, nil
}

// 订单列表
func (o OrderService) OrderList(ctx context.Context, request *order.OrderFilterRequest) (*order.OrderListResponse, error) {
	var orderList []*model.OrderInfo

	//链式
	localDb := global.DB.Model(&model.OrderInfo{})

	//查询条件
	//如果传入了用户id则根据用户ID查列表
	if request.UserId > 0 {
		localDb = localDb.Where(model.OrderInfo{
			UserId: request.UserId,
		})
	}

	//判断支付方式来查询列表
	switch request.PayType {
	case 1:
		localDb = localDb.Where(model.OrderInfo{
			PayType: int8(request.PayType),
		})
	case 2:
		localDb = localDb.Where(model.OrderInfo{
			PayType: int8(request.PayType),
		})
	default:
		break
	}
	//判断支付状态来查询列表
	switch request.Status {
	case 1:
		localDb = localDb.Where(model.OrderInfo{
			Status: int8(request.Status),
		})
	case 2:
		localDb = localDb.Where(model.OrderInfo{
			Status: int8(request.Status),
		})
	case 3:
		localDb = localDb.Where(model.OrderInfo{
			Status: int8(request.Status),
		})
	case 4:
		localDb = localDb.Where(model.OrderInfo{
			Status: int8(request.Status),
		})
	case 5:
		localDb = localDb.Where(model.OrderInfo{
			Status: int8(request.Status),
		})
	default:
		break
	}

	//分页查找
	tx := localDb.Scopes(pkg.Paginate(int(request.Pages), int(request.PagePerNums))).Find(&orderList)
	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "订单列表查询失败")
	}

	var count int64

	localDb.Count(&count)

	var orderListResponse []*order.OrderInfoResponse
	for _, v := range orderList {
		orderListResponse = append(orderListResponse, &order.OrderInfoResponse{
			Id:      int32(v.ID),
			UserId:  v.UserId,
			OrderSn: v.OrderSn,
			PayType: strconv.Itoa(int(v.PayType)),
			Status:  strconv.Itoa(int(v.Status)),
			Post:    v.Post,
			Total:   v.OrderMount,
			Address: v.Address,
			Name:    v.SignerName,
			Mobile:  v.SingerMobile,
		})
	}
	return &order.OrderListResponse{
		Total: int32(count),
		Data:  orderListResponse,
	}, nil
}

// 订单详情
func (o OrderService) OrderDetail(ctx context.Context, request *order.OrderRequest) (*order.OrderInfoDetailResponse, error) {
	var orderInfo model.OrderInfo

	tx := global.DB.Model(&model.OrderInfo{}).Where(model.OrderInfo{
		OrderSn: request.OrderSn,
	}).First(&orderInfo)

	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "订单不存在")
	}

	var orderGoods []*model.OrderGoods

	tx = global.DB.Model(&model.OrderGoods{}).Where(model.OrderGoods{
		OrderId: int32(orderInfo.ID),
	}).Find(&orderGoods)

	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "订单商品不存在")
	}

	var orderGoodsResponse []*order.OrderItemResponse

	for _, v := range orderGoods {
		orderGoodsResponse = append(orderGoodsResponse, &order.OrderItemResponse{
			Id:         int32(v.ID),
			OrderId:    v.OrderId,
			GoodsId:    v.GoodsId,
			GoodsName:  v.GoodsName,
			GoodsImage: v.GoodsImage,
			GoodsPrice: v.GoodsPrice,
			Nums:       v.Nums,
		})
	}

	return &order.OrderInfoDetailResponse{
		OrderInfo: &order.OrderInfoResponse{
			Id:      int32(orderInfo.ID),
			UserId:  orderInfo.UserId,
			OrderSn: orderInfo.OrderSn,
			PayType: strconv.Itoa(int(orderInfo.PayType)),
			Status:  strconv.Itoa(int(orderInfo.Status)),
			Post:    orderInfo.Post,
			Total:   orderInfo.OrderMount,
			Address: orderInfo.Address,
			Name:    orderInfo.SignerName,
			Mobile:  orderInfo.SingerMobile,
		},
		Goods: orderGoodsResponse,
	}, nil
}

// 修改订单信息
func (o OrderService) UpdateOrder(ctx context.Context, info *order.UpdateOrderInfo) (*order.OrderEmpty, error) {
	var orderInfo model.OrderInfo

	tx := global.DB.Model(&model.OrderInfo{}).Where(model.OrderInfo{
		OrderSn: info.OrderSn,
	}).First(&orderInfo)
	if tx.Error != nil {
		return nil, status.Errorf(codes.Internal, "订单不存在")
	}

	paytype := info.PayType
	paytypeInt, _ := strconv.Atoi(paytype)

	statu := info.Status
	stautsInt, _ := strconv.Atoi(statu)

	tx = global.DB.Model(&model.OrderInfo{}).Where(model.OrderInfo{
		OrderSn: info.OrderSn,
	}).Updates(&model.OrderInfo{
		PayType: int8(paytypeInt),
		Status:  int8(stautsInt),
		TradeNo: info.TradeNo,
	})

	return &order.OrderEmpty{}, nil
}

func OrderTimeout(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
	//把msgs的信息反解到订单结构体
	for _, msg := range msgs {
		orderData := model.OrderInfo{}
		json.Unmarshal(msg.Body, &orderData)

		//查询订单是否存在
		tx := global.DB.Model(&model.OrderInfo{}).Where(model.OrderInfo{OrderSn: orderData.OrderSn}).First(&orderData)
		if tx.Error != nil {
			zap.S().Info("订单不存在")
			return consumer.ConsumeRetryLater, nil
		}
		if orderData.Status == 2 {
			return consumer.ConsumeRetryLater, nil
		} else if orderData.Status == 1 {
			orderData.Status = 3
			save := global.DB.Save(&orderData)
			if save.Error != nil {
				zap.S().Info("修改订单状态失败")
				return consumer.ConsumeRetryLater, save.Error
			}
			//归还库存
			_, err := global.InventoryService.Rollback(context.Background(), &inventory.SellInfo{OrderSn: orderData.OrderSn})
			if err != nil {
				zap.S().Info("订单超时归还库存失败")
				return consumer.ConsumeRetryLater, nil
			}
		}
	}
	return consumer.ConsumeSuccess, nil
}
